package com.by.auth;

import com.by.aop.LogAop;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 自定义 FormAuthenticationFilter
 */
@SuppressWarnings("all")
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {

	private Logger log = LoggerFactory.getLogger(LogAop.class);

	/**
	 * 是否强制转向指定successUrl，忽略登录之前自动保存的URL
	 */
	private boolean forceSuccessUrl = true;


    /**
     * 重写生成token的方法
     * @param request
     * @param response
     * @return
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        //用户名
        String username = getUsername(request);
        //密码
        String password = getPassword(request);
        //记住我
        boolean rememberMe = isRememberMe(request);
        //host
        String host = getHost(request);
        //创建host

	    //登录的角色类型
	    String loginType = ServletRequestUtils.getStringParameter(request, "loginType","");

	    MyUsernamePasswordToken token = new MyUsernamePasswordToken(username, password, rememberMe, host, loginType);
        return token;
        //return super.createToken(request, response);
    }

	/**
	 * 用户没登录或者登录失效后，需要需要重新登跳转
	 * 1：判断当前的请求类是否ajax类型
	 *  是 返回json字符串
	 *  否 直接跳转到认证页面
	 *
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	@Override
	protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {

		HttpServletResponse httpServletResponse = (HttpServletResponse) response;
		HttpServletRequest httpServletRequest = (HttpServletRequest) request;

		if(log.isErrorEnabled()) {
			log.error("account need login for: {}",  ((HttpServletRequest)request).getServletPath());
		}

		if (!"XMLHttpRequest".equalsIgnoreCase(httpServletRequest
				.getHeader("X-Requested-With"))) {// 不是ajax请求
			super.redirectToLogin(request, response);
		} else {
			// 请求被拦截后直接返回json格式的响应数据
			response.reset();
			response.setCharacterEncoding("utf-8");
			response.setContentType("application/json");
			PrintWriter out = response.getWriter();
			out.println("{\"code\":403,\"success\":false,\"message\":\"登录认证失效，请重新登录!\"}");
			out.flush();
			out.close();
		}

		//super.redirectToLogin(request, response);
	}

	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
		HttpServletRequest httpServletRequest = (HttpServletRequest)request;

		//UserInfo userInfo = (UserInfo)SecurityUtils.getSubject().getPrincipal();

		boolean result = super.isAccessAllowed(request, response, mappedValue);

        //已经认证过的用户再次，返回到认证页面，再次提交认证请求，依然有认证流程
	    if(isLoginRequest(request,response) && isLoginSubmission(request,response) && result){
            return false;

	    }

		return result;
	}

	@Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        /*Boolean showValidateCode = (Boolean) httpServletRequest.getSession().getAttribute("showValidateCode");

        if(null!=showValidateCode) {
            httpServletRequest.getSession().setAttribute("showValidateCode", showValidateCode);
        }else {
            httpServletRequest.getSession().setAttribute("showValidateCode", !true);
        }*/


        if(isLoginRequest(request, response) && isLoginSubmission(request, response)){
            log.debug("登录名:{}, 密码:{}", getUsername(request), getPassword(request));

        }


        return super.onAccessDenied(request, response);
    }

    /**
     * 执行shiro登录操作
     * 具体代码
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        //
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        ServletWebRequest webRequest = new ServletWebRequest(httpServletRequest, httpServletResponse);

        //构造token
	    MyUsernamePasswordToken token = (MyUsernamePasswordToken) createToken(request, response);

        //用户必须输入用户名和密码
        if(StringUtils.isEmpty(getUsername(request)) || StringUtils.isEmpty(getPassword(request))) {
            return onLoginFailure(token, new AuthenticationException("请输入用户和密码"), request, response);
        }

        try {
            String username = getUsername(request);
            //写入登录账号名称用于回显
            request.setAttribute("username", username);

            //todo:chen 根据用户输入的用户应查询查找用户
            /*if(!false) {
                //判断用户的登录错误的次数是否超出最大次数的限制
                if(!true) {
                    throw  new CaptchaValidationException("验证码错误");
                }
            }*/

            /*Boolean showValidateCode = (Boolean) httpServletRequest.getSession().getAttribute("showValidateCode");
            if(null != showValidateCode && true == showValidateCode) {
                //获取用户输入的验证码
                String validateCode = ServletRequestUtils.getStringParameter(httpServletRequest, "validateCode", "");
                //获取session 中存储的验证码对象
                ShearCaptcha captcha = (ShearCaptcha)httpServletRequest.getSession().getAttribute("ShearCaptcha");

                //比较验证码
                if(!captcha.verify(validateCode)) {
                    throw  new CaptchaValidationException("验证码错误");
                }
            }*/


            Subject subject = getSubject(request, response);
            subject.login(token);
            return onLoginSuccess(token, subject, request, response);
        } catch (AuthenticationException e) {

            return onLoginFailure(token, e, request, response);
        }


        //return super.executeLogin(request, response);
    }

    /**
     * 登录成功后的操作
     * 1：判断请求类型是否是ajax请求
     *  是：返回登入成功的json字符串
     *  否：直接跳转到主页
     * @param token
     * @param subject
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
	    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
	    HttpServletRequest httpServletRequest = (HttpServletRequest) request;

	    boolean isSUPER = subject.hasRole("ROLE_SUPER");
	    boolean isADMIN = subject.hasRole("ROLE_ADMIN");
	    boolean isUSER = subject.hasRole("ROLE_USER");

	    String forwardPath = isSUPER ? "/super/online" : "/admin";

        if (!"XMLHttpRequest".equalsIgnoreCase(httpServletRequest
                .getHeader("X-Requested-With"))) {// 不是ajax请求
            //如果是强制转向指定successUrl则清空SavedRequest
            if (forceSuccessUrl) {
                WebUtils.getAndClearSavedRequest(httpServletRequest);
            }
            if(isSUPER) {
	            httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + forwardPath);
	            return false;
            }

            return super.onLoginSuccess(token, subject, request, response);
        } else {
            httpServletResponse.setCharacterEncoding("UTF-8");
	        httpServletResponse.setContentType("application/json");
            PrintWriter out = httpServletResponse.getWriter();
            out.println("{\"code\":0,\"success\":true,\"message\":\"登入成功\",\"forwardPath\":\""+forwardPath+"\"}");
            out.flush();
            out.close();
        }
        return false;
    }

    /**
     * 登录失败后的操作
     * 1：判断请求类型是否是ajax请求
     *  是：返回登入失败的json字符串
     *  否：直接跳转到认证页面
     * @param token
     * @param e
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;

        if (!isAjax(httpServletRequest)) {// 不是ajax请求
            return super.onLoginFailure(token, e, request, response);
        }
        try {
            response.setCharacterEncoding("UTF-8");
	        response.setContentType("application/json");
            PrintWriter out = response.getWriter();
            String message = e.getClass().getSimpleName();
            out.println("{\"code\":1,\"success\":false,\"message\":\""+e.getMessage()+"\"}");
            out.flush();
            out.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return false;
    }

    /**
     * 重复登录失败后
     * 将错误写入到rqueest中
     * @param request
     * @param ae
     */
    @Override
    protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        //打开验证码
        httpServletRequest.getSession().setAttribute("showValidateCode", !false);

        String exceptionMessage = ae.getMessage();
        request.setAttribute(getFailureKeyAttribute(), exceptionMessage);
        //super.setFailureAttribute(request, ae);
    }

    /**
     * 验证码异常类
     */
    public static class CaptchaValidationException extends AuthenticationException {

        private static final long serialVersionUID = -7285314964501172092L;

        public CaptchaValidationException(String message) {
            super(message);
        }
    }



	public void setForceSuccessUrl(boolean forceSuccessUrl) {
		this.forceSuccessUrl = forceSuccessUrl;
	}


	private boolean isAjax(HttpServletRequest request) {
		String header = request.getHeader("x-requested-with");
		if (null != header && "XMLHttpRequest".endsWith(header)) {
			return true;
		}
		return false;
	}
}
